# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
MQTT客户端抽象类
"""
import _thread
import utime
import checkNet
from umqtt import MQTTClient
from usr import error
from usr.logging import getLogger
from usr.net_manager import NetManager
from usr.serial import Serial
from usr.settings import ConfigureHandler

logger = getLogger(__name__)


class MqttIot(object):
    RECONNECT_WAIT_SECONDS = 20

    def __init__(self, *args, **kwargs):
        self.clean_session = kwargs.pop('clean_session', True)
        self.qos = kwargs.pop('qos', 0)
        self.subscribe_topic = kwargs.pop('subscribe_topic', '/public/test')
        self.publish_topic = kwargs.pop('publish_topic', '/public/test')
        self.queue = kwargs.pop('queue')
        self.error_trans = kwargs.pop('error_trans', False)

        kwargs.setdefault('reconn', False)  # 禁用内部重连机制
        self.cli = MQTTClient(*args, **kwargs)
        config = ConfigureHandler('/usr/dtu_config.json')
        self.serial = Serial(**config.get('uart_config'))

    def init(self):
        self.cli.set_callback(self.callback)
        self.connect()
        self.listen()

    def callback(self, topic, data):
        self.queue.put((topic, data))

    def put_error(self, e):
        if self.error_trans:
            self.queue.put((None, str(e)))

    def connect(self):

        while True:
            # 检查注网和拨号
            if not NetManager.check_and_reconnect():
                logger.error('network status error.')
                continue

            # 重连
            try:
                self.cli.connect()
            except Exception as e:
                logger.error('mqtt connect failed. {}'.format(str(e)))
                self.put_error(error.ConnectError())
                self.serial.write("RECONNECT")
                utime.sleep(self.RECONNECT_WAIT_SECONDS)
                continue

            # 订阅
            try:
                self.cli.subscribe(self.subscribe_topic, self.qos)
            except Exception as e:
                logger.error('mqtt subscribe failed. {}'.format(str(e)))
                self.put_error(error.SubscribeError())
                self.serial.write("SUBFAILED")
                utime.sleep(self.RECONNECT_WAIT_SECONDS)
                continue

            logger.info('mqtt connect successfully!')
            self.serial.write("CONNECT")
            break

    def listen(self):
        _thread.start_new_thread(self.listen_thread_worker, ())

    def listen_thread_worker(self):
        while True:
            try:
                self.cli.wait_msg()
            except Exception as e:
                logger.error('mqtt listen error continue to reconnect. {}'.format(str(e)))
                self.put_error(error.ListenError())
                self.cli.close()
                self.connect()

    def recv(self):
        return self.queue.get()

    def send(self, data):
        try:
            if not self.cli.publish(self.publish_topic, data):
                logger.error('publish failed.')
                self.put_error(error.PublishError())
        except Exception as e:
            logger.error('publish failed with error: {}, prepare to check network.'.format(e))
            NetManager.check_and_reconnect()
